texture: Add gdk_texture_download_texture()
authorBenjamin Otte <otte@redhat.com>
Sun, 12 Sep 2021 02:42:24 +0000 (04:42 +0200)
committerBenjamin Otte <otte@redhat.com>
Sun, 12 Sep 2021 03:22:21 +0000 (05:22 +0200)
A private vfunc that downloads a texture as a GdkMemoryTexture in
whatever format the texture deems best.

There are multiple reasons for this:

 * GLES cannot download the Cairo format. But it can download some
   format and then just delegate to the GdkMemoryTexture implementation.

 * All the other download vfuncs (including the ones still coming) can
   be implemented via download_texture() and delegation, making the
   interface easier.

 * We want to implement image loading and saving support. By using
   download_texture(), we can save in the actual format of the texture.

 * A potential GdkCompressedTexture could be implemented by just
   providing this one vfunc as a compress() step.

gdk/gdkgltexture.c
gdk/gdkmemorytexture.c
gdk/gdktexture.c
gdk/gdktextureprivate.h

index adf075ea913569b6d40cd7d6dbfe0820968b20c1..9b5aa8686647d226483d463b2deb9cec6fb312d1 100644 (file)
@@ -21,7 +21,7 @@
 #include "gdkgltextureprivate.h"
 
 #include "gdkcairo.h"
-#include "gdkmemorytexture.h"
+#include "gdkmemorytextureprivate.h"
 #include "gdktextureprivate.h"
 
 #include <epoxy/gl.h>
@@ -70,6 +70,75 @@ gdk_gl_texture_dispose (GObject *object)
   G_OBJECT_CLASS (gdk_gl_texture_parent_class)->dispose (object);
 }
 
+static GdkTexture *
+gdk_gl_texture_download_texture (GdkTexture *texture)
+{
+  GdkGLTexture *self = GDK_GL_TEXTURE (texture);
+  GdkTexture *result;
+  int active_texture;
+  GdkMemoryFormat format;
+  GLint internal_format, gl_format, gl_type;
+  guchar *data;
+  gsize stride;
+  GBytes *bytes;
+
+  if (self->saved)
+    return g_object_ref (self->saved);
+
+  gdk_gl_context_make_current (self->context);
+
+  glGetIntegerv (GL_TEXTURE_BINDING_2D, &active_texture);
+  glBindTexture (GL_TEXTURE_2D, self->id);
+
+  glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
+
+  switch (internal_format)
+  {
+    case GL_RGB8:
+      format = GDK_MEMORY_R8G8B8;
+      gl_format = GL_RGB;
+      gl_type = GL_UNSIGNED_BYTE;
+      break;
+
+    case GL_RGBA8:
+      format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
+      gl_format = GL_RGBA;
+      gl_type = GL_UNSIGNED_BYTE;
+      break;
+
+    default:
+      g_warning ("Texture in unexpected format 0x%X (%d). File a bug about adding it to GTK", internal_format, internal_format);
+      /* fallback to the dumbest possible format
+       * so that even age old GLES can do it */
+      format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
+      gl_format = GL_RGBA;
+      gl_type = GL_UNSIGNED_BYTE;
+      break;
+  }
+
+  stride = gdk_memory_format_bytes_per_pixel (format) * texture->width;
+  data = g_malloc (stride * texture->height);
+
+  glGetTexImage (GL_TEXTURE_2D,
+                 0,
+                 gl_format,
+                 gl_type,
+                 data);
+
+  bytes = g_bytes_new_take (data, stride * texture->height);
+  result = gdk_memory_texture_new (texture->width,
+                                   texture->height,
+                                   format,
+                                   bytes,
+                                   stride);
+
+  g_bytes_unref (bytes);
+
+  glBindTexture (GL_TEXTURE_2D, active_texture);
+
+  return result;
+}
+
 static void
 gdk_gl_texture_download (GdkTexture *texture,
                          guchar     *data,
@@ -112,6 +181,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass)
   GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+  texture_class->download_texture = gdk_gl_texture_download_texture;
   texture_class->download = gdk_gl_texture_download;
   gobject_class->dispose = gdk_gl_texture_dispose;
 }
index ab117a01ff9c6b4f6c7d604f758617148060ac69..edb4d741c608244a3e139a3d622e9a32a2648af0 100644 (file)
@@ -79,6 +79,12 @@ gdk_memory_texture_dispose (GObject *object)
   G_OBJECT_CLASS (gdk_memory_texture_parent_class)->dispose (object);
 }
 
+static GdkTexture *
+gdk_memory_texture_download_texture (GdkTexture *texture)
+{
+  return g_object_ref (texture);
+}
+
 static void
 gdk_memory_texture_download (GdkTexture *texture,
                              guchar     *data,
@@ -101,6 +107,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
   GdkTextureClass *texture_class = GDK_TEXTURE_CLASS (klass);
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+  texture_class->download_texture = gdk_memory_texture_download_texture;
   texture_class->download = gdk_memory_texture_download;
   gobject_class->dispose = gdk_memory_texture_dispose;
 }
index 9ae9f80944f475c8e35522eb822948c3878583f4..4afd6ebfdd28ac642aea56a1d258f7e3bfa04de5 100644 (file)
@@ -115,12 +115,23 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT,
 #define GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
   g_critical ("Texture of type '%s' does not implement GdkTexture::" # method, G_OBJECT_TYPE_NAME (obj))
 
+static GdkTexture *
+gdk_texture_real_download_texture (GdkTexture *self)
+{
+  GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download_texture);
+  return NULL;
+}
+
 static void
-gdk_texture_real_download (GdkTexture         *self,
-                           guchar             *data,
-                           gsize               stride)
+gdk_texture_real_download (GdkTexture *texture,
+                           guchar     *data,
+                           gsize      stride)
 {
-  GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download);
+  GdkTexture *memory_texture;
+
+  memory_texture = gdk_texture_download_texture (texture);
+  gdk_texture_download (memory_texture, data, stride);
+  g_object_unref (memory_texture);
 }
 
 static void
@@ -186,6 +197,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+  klass->download_texture = gdk_texture_real_download_texture;
   klass->download = gdk_texture_real_download;
 
   gobject_class->set_property = gdk_texture_set_property;
@@ -473,6 +485,22 @@ gdk_texture_download (GdkTexture *texture,
   GDK_TEXTURE_GET_CLASS (texture)->download (texture, data, stride);
 }
 
+GdkTexture *
+gdk_texture_download_texture (GdkTexture *texture)
+{
+  g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
+
+  g_object_ref (texture);
+  while (!GDK_IS_MEMORY_TEXTURE (texture))
+    {
+      GdkTexture *downloaded = GDK_TEXTURE_GET_CLASS (texture)->download_texture (texture);
+      g_object_unref (texture);
+      texture = downloaded;
+    }
+
+  return texture;
+}
+
 gboolean
 gdk_texture_set_render_data (GdkTexture     *self,
                              gpointer        key,
index e2e7dc2bae71a6550a742be9951a6f9c33c46a2d..1133bf6acc17335214fc1decd61faa1cd5e4317b 100644 (file)
@@ -24,6 +24,9 @@ struct _GdkTexture
 struct _GdkTextureClass {
   GObjectClass parent_class;
 
+  /* mandatory: Download into a GdkMemoryTexture */
+  GdkTexture *          (* download_texture)            (GdkTexture             *texture);
+  /* optional */
   void                  (* download)                    (GdkTexture             *texture,
                                                          guchar                 *data,
                                                          gsize                   stride);
@@ -34,6 +37,8 @@ gpointer                gdk_texture_new                 (const GdkTextureClass
                                                          int                     height);
 GdkTexture *            gdk_texture_new_for_surface     (cairo_surface_t        *surface);
 cairo_surface_t *       gdk_texture_download_surface    (GdkTexture             *texture);
+/* NB: GdkMemoryTexture */
+GdkTexture *            gdk_texture_download_texture    (GdkTexture             *texture);
 
 gboolean                gdk_texture_set_render_data     (GdkTexture             *self,
                                                          gpointer                key,